package LPU;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import termWeighting.ProposedTermWeighting;
import util.BugListProcess;
import util.ListUtil;
import util.MathUtil;
import util.ReadSummary;
import ds.BugInfo;
import ds.Constants;

public class FirstStepForLPU {
	public HashMap<String, String> bugId2Class;
	public ArrayList<BugInfo> pos;
	public ArrayList<BugInfo> nonPos;
	
	public ArrayList<BugInfo> LPU_pos;
	public ArrayList<BugInfo> LPU_unlabel;
	public ArrayList<BugInfo> LPU_test;
	
	public FirstStepForLPU(HashMap<String, String> categoryMap){
		bugId2Class = new HashMap<String, String>();
		bugId2Class.putAll(categoryMap);
		
		this.pos = new ArrayList<BugInfo>();	
		this.nonPos = new ArrayList<BugInfo>();
		
		LPU_pos = new ArrayList<BugInfo>();
		LPU_unlabel = new ArrayList<BugInfo>();
		LPU_test = new ArrayList<BugInfo>();
	}
	
	public ArrayList<BugInfo> run(String TFfile){
		getBugs(TFfile);
		divideDataSet(0.3);
		
		ArrayList<Double> posProto = calculateProto(this.LPU_pos);
		ArrayList<Double> negaProto = calculateProto(this.LPU_unlabel);
		ArrayList<Double> posProtoCopy = new ArrayList<Double>(posProto);
		ArrayList<Double> negaProtoCopy = new ArrayList<Double>(negaProto);
		posProto = ListUtil.minus(ListUtil.multiply(posProto, 16), ListUtil.multiply(negaProtoCopy, 4));
		negaProto = ListUtil.minus(ListUtil.multiply(negaProto, 16), ListUtil.multiply(posProtoCopy, 4));
		ArrayList<BugInfo> rn = getReliableNega(LPU_unlabel, posProto, negaProto);	
		
		return rn;
	}
	
	public void getBugs(String FileName){
		try {
			BufferedReader readTF = new BufferedReader(new FileReader(FileName));
			String summary = readTF.readLine();
			int[] values = ReadSummary.readTfFileSummary(summary);
			int wordCount = values[0];
			int posCount = values[1];
			int nonPosCount = values[2];
			
			for(int i = 0;  i < posCount + nonPosCount; i++){				
				String[] words = readTF.readLine().split(" ");
				String bugId = words[0].substring(0, words[0].length()-1);
				
				BugInfo CurrentDoc = new BugInfo();
				for(int count = 0; count < wordCount; count++)
					CurrentDoc.termWeight.add(0.0);
				CurrentDoc.bugID = bugId;
				CurrentDoc.label= bugId2Class.get(bugId);			
				for(int j=1; j<words.length; j++){
					int wordIndex = Integer.parseInt(words[j].split(":")[0]);
					Double wordTF = Double.parseDouble(words[j].split(":")[1]);
					CurrentDoc.wordId2TF.put(wordIndex, wordTF);
					CurrentDoc.termWeight.set(wordIndex, wordTF);
				}		
				
				if(i < posCount)
					this.pos.add(CurrentDoc);
				else
					this.nonPos.add(CurrentDoc);
			}
			readTF.close();
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
	
	public void divideDataSet(double testRate){
		ArrayList<BugInfo> negative = new ArrayList<BugInfo>();
		for(BugInfo curNon: nonPos){
			if(curNon.label.equals(""))
				LPU_unlabel.add(curNon);
			else
				negative.add(curNon);
		}
		
		ArrayList<ArrayList<BugInfo>> posTrainAndTest = BugListProcess.divideDataSet(pos, testRate);
		ArrayList<ArrayList<BugInfo>> negaTrainAndTest = BugListProcess.divideDataSet(negative, testRate);
		
		LPU_pos.addAll(posTrainAndTest.get(0));
		LPU_test.addAll(posTrainAndTest.get(1));
		LPU_test.addAll(negaTrainAndTest.get(1));
	}
	
	public ArrayList<Double> calculateProto(ArrayList<BugInfo> bugs){
		ArrayList<Double> proto = new ArrayList<Double>(bugs.get(0).termWeight);
		proto = ListUtil.setZero(proto);
		
		for(BugInfo bug : bugs){
			ArrayList<Double> weights = new ArrayList<Double>(bug.termWeight);
			double length = MathUtil.calculateLength(weights);
			weights = ListUtil.divide(weights, length);
			proto = ListUtil.add(proto, weights);
		}
		proto = ListUtil.divide(proto, bugs.size());
		
		return proto;
	}

	public ArrayList<BugInfo> getReliableNega(ArrayList<BugInfo> bugs, ArrayList<Double> posProto, ArrayList<Double> negaProto){
		ArrayList<BugInfo> rn = new ArrayList<BugInfo>();
		
		for(BugInfo bug : bugs){
			double posSimi = calculateSimi(posProto, bug.termWeight);
			double negaSimi = calculateSimi(negaProto, bug.termWeight);
			if(negaSimi > posSimi)
				rn.add(bug);
		}
		
		return rn;
	}
	
	public double calculateSimi(ArrayList<Double> proto, ArrayList<Double> target){
		double simi = 0;
		for(int i=0; i<proto.size(); i++){
			simi += proto.get(i) * target.get(i);
		}
		simi = simi / (MathUtil.calculateLength(proto) * MathUtil.calculateLength(target));
		return simi;
	}
}

